package com.berchen.原子变量和CAS算法;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 一、i++的原子性问题：i++操作分为三个步骤：读 改 写
 * i++底层：
 * int temp=i;
 * i=i+1;
 * i=temp;
 *
 *
 * 程序运行结果：
 *  Thread-1:0
 * Thread-0:0
 * Thread-7:1
 * Thread-9:5
 * Thread-3:4
 * Thread-5:8
 * Thread-6:2
 * Thread-8:3
 * Thread-4:7
 * Thread-2:6
 *
 * 本来应该是0-9但是这里的数据出现了问题，重复了0.这是为什么呢？
 * 因为线程一读取了seriaNumber=0.并且进行的读改。这时候线程一的缓存中seriaNumber=1.还没有往主存中写。
 * 这时候线程而读取到了seriaNumber=0.线程而将执行线程一一样的操作。这时候就打印了两次。就出现了这个问题，
 * 内存可见性问题，可以用volatile关键字解决。但是这里是变量的原子性。volatile不能解决。
 *
 *
 * 那么我们应该怎么解决呢？
 *
 * 一、使用原子变量：jdk1.5之后在java.util.concurrent.atomic包下提供了常用的原子变量：AtomicInteger 等等
 *      1、volatile 保证内存可见性。
 *      2、CAS（Compare-And-Swap）算法 保证原子可见性。
 *          CAS算法：是硬件对于并发操作共享数据的支持。
 *          CAS包含了三个操作数：
 *              内存值 V
 *              预估值 A
 *              更新值 B
 *              当且仅当V==A时，才V=B，否则什么操作都不做。
 *      3、CAS算法比同步锁效率高。因为CAS算法不成功的时候线程可以继续对数据进行CAS计算。不会阻塞。
 *
 * 二、同步锁。
 */
public class TestAtomicDemo {
    public static void main(String[] args) {

        AtomicDemo tb=new AtomicDemo();

        for(int i=0;i<10;i++){

            new Thread(tb).start();
        }
    }
}

class AtomicDemo implements Runnable {

//    private int seriaNumber = 0;
    private AtomicInteger seriaNumber = new AtomicInteger(0);

    @Override
    public void run() {

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + getSeriaNumber());
    }

    public int getSeriaNumber() {
//        return seriaNumber++;
        return seriaNumber.getAndIncrement();   // 先获取。然后再++，直接调用的是原子变量AtomicInteger的方法。
    }
}
